home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / VideoSequenceCompressor.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-20  |  9.9 KB  |  364 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include "VirtualDub.h"
  19. #include <crtdbg.h>
  20. #include <windows.h>
  21. #include <vfw.h>
  22.  
  23. #include "VideoSequenceCompressor.h"
  24. #include "Error.h"
  25. #include "crash.h"
  26.  
  27. //////////////////////////////////////////////////////////////////////////////
  28. //
  29. //    IMITATING WIN2K AVISAVEV() BEHAVIOR IN 0x7FFFFFFF EASY STEPS
  30. //
  31. //    ICM_COMPRESS_FRAMES_INFO:
  32. //
  33. //        dwFlags            Trashed with address of lKeyRate in tests. Something
  34. //                        might be looking for a non-zero value here, so better
  35. //                        set it.
  36. //        lpbiOutput        NULL.
  37. //        lOutput            0.
  38. //        lpbiInput        NULL.
  39. //        lInput            0.
  40. //        lStartFrame        0.
  41. //        lFrameCount        Number of frames.
  42. //        lQuality        Set to quality factor, or zero if not supported.
  43. //        lDataRate        Set to data rate in 1024*kilobytes, or zero if not
  44. //                        supported.
  45. //        lKeyRate        Set to the desired maximum keyframe interval.  For
  46. //                        all keyframes, set to 1.        
  47. //
  48. //    ICM_COMPRESS:
  49. //
  50. //        dwFlags            Equal to ICCOMPRESS_KEYFRAME if a keyframe is
  51. //                        required, and zero otherwise.
  52. //        lpckid            Always points to zero.
  53. //        lpdwFlags        Points to AVIIF_KEYFRAME if a keyframe is required,
  54. //                        and zero otherwise.
  55. //        lFrameNum        Ascending from zero.
  56. //        dwFrameSize        Always set to 7FFFFFFF (Win9x) or 00FFFFFF (WinNT)
  57. //                        for first frame.  Set to zero for subsequent frames
  58. //                        if data rate control is not active or not supported,
  59. //                        and to the desired frame size in bytes if it is.
  60. //        dwQuality        Set to quality factor from 0-10000 if quality is
  61. //                        supported.  Otherwise, it is zero.
  62. //        lpbiPrev        Set to NULL if not required.
  63. //        lpPrev            Set to NULL if not required.
  64. //
  65. //////////////////////////////////////////////////////////////////////////////
  66.  
  67. VideoSequenceCompressor::VideoSequenceCompressor() {
  68.     pPrevBuffer        = NULL;
  69.     pOutputBuffer    = NULL;
  70.     pConfigData        = NULL;
  71. }
  72.  
  73. VideoSequenceCompressor::~VideoSequenceCompressor() {
  74.  
  75.     freemem(pbiInput);
  76.     freemem(pbiOutput);
  77.  
  78.     finish();
  79.  
  80.     delete pConfigData;
  81.     delete pOutputBuffer;
  82.     delete pPrevBuffer;
  83. }
  84.  
  85. void VideoSequenceCompressor::init(HIC hic, BITMAPINFO *pbiInput, BITMAPINFO *pbiOutput, long lQ, long lKeyRate) {
  86.     ICINFO    info;
  87.     LRESULT    res;
  88.     int cbSizeIn, cbSizeOut;
  89.  
  90.     cbSizeIn = pbiInput->bmiHeader.biSize + pbiInput->bmiHeader.biClrUsed*4;
  91.     cbSizeOut = pbiOutput->bmiHeader.biSize + pbiOutput->bmiHeader.biClrUsed*4;
  92.  
  93.     this->hic        = hic;
  94.     this->pbiInput    = (BITMAPINFO *)allocmem(cbSizeIn);
  95.     this->pbiOutput    = (BITMAPINFO *)allocmem(cbSizeOut);
  96.     this->lKeyRate    = lKeyRate;
  97.  
  98.     memcpy(this->pbiInput, pbiInput, cbSizeIn);
  99.     memcpy(this->pbiOutput, pbiOutput, cbSizeOut);
  100.  
  101.     lKeyRateCounter = 1;
  102.  
  103.     // Retrieve compressor information.
  104.  
  105.     res = ICGetInfo(hic, &info, sizeof info);
  106.  
  107.     if (!res)
  108.         throw MyError("Unable to retrieve video compressor information.");
  109.  
  110.     // Analyze compressor.
  111.  
  112.     this->dwFlags = info.dwFlags;
  113.  
  114.     if (info.dwFlags & VIDCF_TEMPORAL) {
  115.         if (!(info.dwFlags & VIDCF_FASTTEMPORALC)) {
  116.             // Allocate backbuffer
  117.  
  118.             if (!(pPrevBuffer = new char[pbiInput->bmiHeader.biSizeImage]))
  119.                 throw MyMemoryError();
  120.         }
  121.     }
  122.  
  123.     if (info.dwFlags & VIDCF_QUALITY)
  124.         lQuality = lQ;
  125.     else
  126.         lQuality = 0;
  127.  
  128.     // Allocate destination buffer
  129.  
  130.     lMaxPackedSize = ICCompressGetSize(hic, pbiInput, pbiOutput);
  131.  
  132.     if (!(pOutputBuffer = new char[lMaxPackedSize]))
  133.         throw MyMemoryError();
  134.  
  135.     // Save configuration state.
  136.     //
  137.     // Ordinarily, we wouldn't do this, but there seems to be a bug in
  138.     // the Microsoft MPEG-4 compressor that causes it to reset its
  139.     // configuration data after a compression session.  This occurs
  140.     // in all versions from V1 through V3.
  141.     //
  142.     // Stupid fscking Matrox driver returns -1!!!
  143.  
  144.     cbConfigData = ICGetStateSize(hic);
  145.  
  146.     if (cbConfigData > 0) {
  147.         if (!(pConfigData = new char[cbConfigData]))
  148.             throw MyMemoryError();
  149.  
  150.         cbConfigData = ICGetState(hic, pConfigData, cbConfigData);
  151.  
  152.         // As odd as this may seem, if this isn't done, then the Indeo5
  153.         // compressor won't allow data rate control until the next
  154.         // compression operation!
  155.  
  156.         if (cbConfigData)
  157.             ICSetState(hic, pConfigData, cbConfigData);
  158.     }
  159.  
  160.     lMaxFrameSize = 0;
  161.     lSlopSpace = 0;
  162. }
  163.  
  164. void VideoSequenceCompressor::setDataRate(long lDataRate, long lUsPerFrame, long lFrameCount) {
  165.  
  166.     if (lDataRate && (dwFlags & VIDCF_CRUNCH))
  167.         lMaxFrameSize = MulDiv(lDataRate, lUsPerFrame, 1000000);
  168.     else
  169.         lMaxFrameSize = 0;
  170.  
  171.     // Indeo 5 needs this message for data rate clamping.
  172.  
  173.     // The Morgan codec requires the message otherwise it assumes 100%
  174.     // quality :(
  175.  
  176.     // The original version (2700) MPEG-4 V1 requires this message, period.
  177.     // V3 (DivX) gives crap if we don't send it.  So special case it.
  178.  
  179.     ICINFO ici;
  180.  
  181.     ICGetInfo(hic, &ici, sizeof ici);
  182.  
  183.     {
  184.         ICCOMPRESSFRAMES icf;
  185.  
  186.         memset(&icf, 0, sizeof icf);
  187.  
  188.         icf.dwFlags        = (DWORD)&icf.lKeyRate;
  189.         icf.lStartFrame = 0;
  190.         icf.lFrameCount = lFrameCount;
  191.         icf.lQuality    = lQuality;
  192.         icf.lDataRate    = lDataRate;
  193.         icf.lKeyRate    = lKeyRate;
  194.         icf.dwRate        = 1000000;
  195.         icf.dwScale        = lUsPerFrame;
  196.  
  197.         ICSendMessage(hic, ICM_COMPRESS_FRAMES_INFO, (WPARAM)&icf, sizeof(ICCOMPRESSFRAMES));
  198.     }
  199. }
  200.  
  201. void VideoSequenceCompressor::start() {
  202.     LRESULT    res;
  203.  
  204.     // Start compression process
  205.  
  206.     res = ICCompressBegin(hic, pbiInput, pbiOutput);
  207.  
  208.     if (res != ICERR_OK)
  209.         throw MyICError("Unable to start video compression", res);
  210.  
  211.     // Start decompression process if necessary
  212.  
  213.     if (pPrevBuffer) {
  214.         res = ICDecompressBegin(hic, pbiOutput, pbiInput);
  215.  
  216.         if (res != ICERR_OK) {
  217.             ICCompressEnd(hic);
  218.             throw MyICError("Unable to start video compression", res);
  219.         }
  220.     }
  221.  
  222.     fCompressionStarted = true;
  223.     lFrameNum = 0;
  224. }
  225.  
  226. void VideoSequenceCompressor::finish() {
  227.     if (!fCompressionStarted)
  228.         return;
  229.  
  230.     if (pPrevBuffer)
  231.         ICDecompressEnd(hic);
  232.  
  233.     ICCompressEnd(hic);
  234.  
  235.     fCompressionStarted = false;
  236.  
  237.     // Reset MPEG-4 compressor
  238.  
  239.     if (cbConfigData && pConfigData)
  240.         ICSetState(hic, pConfigData, cbConfigData);
  241. }
  242.  
  243. void VideoSequenceCompressor::dropFrame() {
  244.     if (lKeyRate && lKeyRateCounter>1)
  245.         --lKeyRateCounter;
  246.  
  247.     ++lFrameNum;
  248. }
  249.  
  250. void *VideoSequenceCompressor::packFrame(void *pBits, bool *pfKeyframe, long *plSize) {
  251.     DWORD dwChunkId = 0;
  252.     DWORD dwFlags=0, dwFlagsIn = ICCOMPRESS_KEYFRAME;
  253.     DWORD res;
  254.     DWORD sizeImage;
  255.     long lAllowableFrameSize=0;//xFFFFFF;    // yes, this is illegal according
  256.                                             // to the docs (see below)
  257.  
  258.     // Figure out if we should force a keyframe.  If we don't have any
  259.     // keyframe interval, force only the first frame.  Otherwise, make
  260.     // sure that the key interval is lKeyRate or less.  We count from
  261.     // the last emitted keyframe, since the compressor can opt to
  262.     // make keyframes on its own.
  263.  
  264.     if (!lKeyRate) {
  265.         if (lFrameNum)
  266.             dwFlagsIn = 0;
  267.     } else {
  268.         if (--lKeyRateCounter)
  269.             dwFlagsIn = 0;
  270.         else
  271.             lKeyRateCounter = lKeyRate;
  272.     }
  273.  
  274.     // Figure out how much space to give the compressor, if we are using
  275.     // data rate stricting.  If the compressor takes up less than quota
  276.     // on a frame, save the space for later frames.  If the compressor
  277.     // uses too much, reduce the quota for successive frames, but do not
  278.     // reduce below half datarate.
  279.  
  280.     if (lMaxFrameSize) {
  281.         lAllowableFrameSize = lMaxFrameSize + (lSlopSpace>>2);
  282.  
  283.         if (lAllowableFrameSize < (lMaxFrameSize>>1))
  284.             lAllowableFrameSize = lMaxFrameSize>>1;
  285.     }
  286.  
  287.     // A couple of notes:
  288.     //
  289.     //    o  ICSeqCompressFrame() passes 0x7FFFFFFF when data rate control
  290.     //       is inactive.  Docs say 0.  We pass 0x7FFFFFFF here to avoid
  291.     //       a bug in the Indeo 5 QC driver, which page faults if
  292.     //       keyframe interval=0 and max frame size = 0.
  293.  
  294.     sizeImage = pbiOutput->bmiHeader.biSizeImage;
  295.  
  296. //    pbiOutput->bmiHeader.biSizeImage = 0;
  297.  
  298.     // Compress!
  299.  
  300.     if (dwFlagsIn)
  301.         dwFlags = AVIIF_KEYFRAME;
  302.  
  303.     VDCHECKPOINT;
  304.  
  305.     res = ICCompress(hic, dwFlagsIn,
  306.             (LPBITMAPINFOHEADER)pbiOutput, pOutputBuffer,
  307.             (LPBITMAPINFOHEADER)pbiInput, pBits,
  308.             &dwChunkId,
  309.             &dwFlags,
  310.             lFrameNum,
  311.             lFrameNum ? lAllowableFrameSize : 0xFFFFFF,
  312.             lQuality,
  313.             dwFlagsIn & ICCOMPRESS_KEYFRAME ? NULL : (LPBITMAPINFOHEADER)pbiInput,
  314.             dwFlagsIn & ICCOMPRESS_KEYFRAME ? NULL : pPrevBuffer);
  315.  
  316.     VDCHECKPOINT;
  317.  
  318.     _RPT2(0,"Compressed frame %d: %d bytes\n", lFrameNum, pbiOutput->bmiHeader.biSizeImage);
  319.  
  320.     ++lFrameNum;
  321.  
  322.     *plSize = pbiOutput->bmiHeader.biSizeImage;
  323.  
  324.     // If we're using a compressor with a stupid algorithm (Microsoft Video 1),
  325.     // we have to decompress the frame again to compress the next one....
  326.  
  327.     if (res==ICERR_OK && pPrevBuffer && (!lKeyRate || lKeyRateCounter>1)) {
  328.  
  329.         VDCHECKPOINT;
  330.  
  331.         res = ICDecompress(hic, dwFlags & AVIIF_KEYFRAME ? 0 : ICDECOMPRESS_NOTKEYFRAME
  332.                 ,(LPBITMAPINFOHEADER)pbiOutput
  333.                 ,pOutputBuffer
  334.                 ,(LPBITMAPINFOHEADER)pbiInput
  335.                 ,pPrevBuffer);
  336.  
  337.         VDCHECKPOINT;
  338.     }
  339.  
  340.     pbiOutput->bmiHeader.biSizeImage = sizeImage;
  341.  
  342.     if (res != ICERR_OK)
  343.         throw MyICError("Video compression", res);
  344.  
  345.     // Update quota.
  346.  
  347.     if (lMaxFrameSize) {
  348.         lSlopSpace += lMaxFrameSize - *plSize;
  349.  
  350.         _RPT3(0,"Compression: allowed %d, actual %d, slop %+d\n", lAllowableFrameSize, *plSize, lSlopSpace);
  351.     }
  352.  
  353.     // Was it a keyframe?
  354.  
  355.     if (dwFlags & AVIIF_KEYFRAME) {
  356.         *pfKeyframe = true;
  357.         lKeyRateCounter = lKeyRate;
  358.     } else {
  359.         *pfKeyframe = false;
  360.     }
  361.  
  362.     return pOutputBuffer;
  363. }
  364.